#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         SingleAssessmentReport.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  This file defines the abstract SingleAssessmentReport class, the 
#               base class of all Model reports that are only for 
#               a single assessment
#
# See Also:     Report, MultipleAssessmentReport
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Report import Report, ReportInfo
from Products.KnowledgeEngine.Model.Model import Model
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.EventDefinition import *

#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from App.ImageFile import ImageFile
from Acquisition import *
from DateTime import DateTime

#####################################################################
## Python Library Imports
# (none)

#####################################################################
## SingleAssessmentReportInfo Class Definition
class SingleAssessmentReportInfo(ReportInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """
    
    #---------------------------------------------------------------#
    
    def getClassName(self):
        """
            Returns the name of the class for which 
            we are providing information.
        """
        return 'SingleAssessmentReport'

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """
    
        return "SingleAssessmentReport"
        
    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.  
        """
        
        return "singleassessmentreport"

    #---------------------------------------------------------------#

    def isInstanceOf(self, className):
        """
            Returns if the class is an instance of the specified
            className, or implements the interface specified 
            by the className
        """

        return className == 'SingleAssessmentReport' or ReportInfo.isInstanceOf(self, className)
    
    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class properties for reflection
        ReportInfo.onConfigureProperties(self)

        self.addPropertyDefinition(
            PropertyDefinition(
                identity="isGlobalReport",
                displayName="Global Report",
                description="Share this report within all child repositories",
                defaultValue=False,
                propertyType=pt_BOOLEAN,
                propertyStructure=ps_ATOMIC,
                propertyFormat=pf_NATIVE
            )
        )

        
     
     #---------------------------------------------------------------#
 
    def onConfigureEvents(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific events using the EventReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class events for reflection
        ReportInfo.onConfigureEvents(self)
    
    #---------------------------------------------------------------#

    def onConfigurePermissions(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific permission definitions.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """
        

        #initialise the super-class permissions for reflection
        ReportInfo.onConfigurePermissions(self)
        
    #---------------------------------------------------------------#

#####################################################################
## SingleAssessmentReport Class Definition
class SingleAssessmentReport(Report):
    """
        Base class for all KnowledgeEngine SingleAssessmentReports
    """

    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = SingleAssessmentReportInfo()

    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the Report"        
        
        Report.__init__(self, id, identity)

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """

        #preform default onAfterInstanciation processing
        Report.onAfterInstanciation(self)
        
        ###print "DEBUG: SingleAssessmentReport.onAfterInstanciation: Completed"
        
    #---------------------------------------------------------------#

    def getReportTitleForAssessment(self, assessment):
        """
            Returns the report title for the specified assessment
            so that it may be displayed in areas like the 
            MyKnowledge Area and/or the AssessmentExplorer.
            
            NOTE: This method may be overriden to enable
            customised report titles.  eg: generating a title
            that contains a score or pass/fail.
        """
        
        #by default, we just output the title or id
        return self.getTitleOrId()
        
    #---------------------------------------------------------------#

    def getDefaultExitURL(self, request):
        """
            ABSTRACT: Returns the default URL to be used
            when exiting a report            
        """

        ###determine the exit URL (where to go if we close/cancel this report)
        portal = self.portal_url.getPortalObject()
        return '%s/myknowledge' % (portal.absolute_url())
        
    #---------------------------------------------------------------#

    def prepareDefaultWizardState(self, request, wizardState):
        """
            Prepares and adds any necessary additional
            state from the request to the wizard state.
            
            This method is called prior to initialise wizardState
            prior to generating a report and prior to attempting
            to collect wizard state from the wizard steps.
            
            You SHOULD capture any properties from the request HERE
            if you want to pass then to a report.

        """

        #capture the assessment identity and place it in the wizard state
        #so we don't lose it when stepping through wizard steps

        ###determine the assessmentIdentity we are to use for this assessment
        #first try the request
        if request.has_key("assessmentIdentity"):
            assessmentIdentity = request["assessmentIdentity"]

        #assessmentIdentity isn't found!
        else:
            assessmentIdentity = None

        #set in the wizard state
        wizardState.setPropertyValue("assessmentIdentity", assessmentIdentity)
        
    #---------------------------------------------------------------#

    def renderReport(self, request, wizardState, **args):
        """
            Renders the report content (called by Report.generate)
            
            NOTE: Override this method to perform specific
            report generation/rendering using the provided
            parameters
        """
    
        #get the assessment storage service... we'll need that to manage the assessment
        storageService = getAssessmentStorageService(self)
    
        #get the security service... so we can check security
        securityService = getSecurityService(self)

        #get the exitURL (will always be valid)
        exitURL = wizardState.getPropertyValue("exitURL")
        
        #get the repository the report is being opened from (may be None)
        openedFromRepositoryIdentity = wizardState.getPropertyValue("openedFromRepositoryIdentity")

        #get the current user that is rendering the report
        user = request["USER"]
    
        #get the session from the request
        session = request["SESSION"]
        
        #get the response from the request (weird I know, but that's where it lives in Zope)
        response = request["RESPONSE"]
    
        ###determine the assessmentIdentity we are to use for this assessment
        assessmentIdentity = wizardState.getPropertyValue("assessmentIdentity")

        #attempt to load the assessment
        if assessmentIdentity is not None:
            #load the assessment
            assessment = storageService.loadAssessment(assessmentIdentity, self.getModel(), user.getIdentity(), openedFromRepositoryIdentity)
        else:
            assessment = None
        
        #did we find the assessment?
        if assessment is None:
            #we must have a specified assessment to view the report for!
            failureMessage = "Sorry.+The+assessment+you+attempted+to+view+the+report+for+does+not+exist"
            exitURL = exitURL + iif(exitURL.find('?') < 0, '?', '&') + 'portal_status_message=' + failureMessage
            return response.redirect(exitURL)

        #ensure the assessment is in the repository (if the report is in the repository)
        if self.getParent().isInstanceOf("Repository"):
            #ensure the assessment is actually in the repository
            if not storageService.isAssessmentInRepository(assessment, self.getParent().getIdentity()):
                failureMessage = "Sorry.+The+selected+assessment+is+no+longer+in+the+repository+'%s'" % (self.getParent().getTitleOrId(),)
                exitURL = exitURL + iif(exitURL.find('?') < 0, '?', '&') + 'portal_status_message=' + failureMessage
                return response.redirect(exitURL)
            
            #ensure the assessment is in scope
            accessRepositoryPermission = securityService.getEffectivePermission("AccessRepository", self.getParent().getIdentity(), user.getIdentity())        
            if not storageService.isAssessmentInScope(assessment, accessRepositoryPermission.getAssessmentScope(), user.getIdentity()):
                failureMessage = "Sorry.+You+no+longer+have+permission+to+the+selected+assessment+in+the+repository+'%s'" % (self.getParent().getTitleOrId(),)
                exitURL = exitURL + iif(exitURL.find('?') < 0, '?', '&') + 'portal_status_message=' + failureMessage
                return response.redirect(exitURL)
    
        #set the assessment we for the report 
        request["ASSESSMENT"] = assessment

        #handle the 'onGenerate' event for the report
        if self.isEventEnabled("onGenerate"):
            self.getEnabledEvent("onGenerate").execute(assessment, user)

        
        return self.callReportTemplate( request=request, **args )
        
    #---------------------------------------------------------------#        
    
    def callReportTemplate( self, request, **args ):
        """ Calls the Page Template (Or other object) for this report """
        
        print "Executing: self.%s_report(self, REQUEST=request, **args)" % self.getClassInfo().getClassName()
        
        #now forward the request to generate the report
        return eval("self.%s_report(self, REQUEST=request, **args)" % self.getClassInfo().getClassName())
            
        
    #---------------------------------------------------------------#
    
    def getReportClass(self):
        """ Returns the reports class name (for use selecting templates to use """
        return self.getClassInfo().getClassName()
    
    
    #---------------------------------------------------------------#

    def renderReportFor(self, genericObject, request, **args):
        """
            Renders the report content for the specified generic object
            and the type of report
        """
        #add the specified genericObject to the request
        request['GENERICOBJECT'] = genericObject
        
        
        # 20061011 [johnm]  Use the defined replacement report display template is available
        if hasattr(genericObject, 'getReportDisplayTemplate') and genericObject.getReportDisplayTemplate():
            return eval("self.%s(self, REQUEST=request, **args)" % genericObject.getReportDisplayTemplate())

        
        #dynamically determine the report page template for the specified object
        return eval("self.%s_%s(self, REQUEST=request, **args)" % (genericObject.getClassInfo().getClassName(), self.getReportClass()))
        
    #---------------------------------------------------------------#
    
    def getIsGlobalReport(self):
        """ Is this report global? """
        
        return getattr(self, 'isGlobalReport', False)
    
    def setIsGlobalReport(self, value):
        """ Sets if this report is global """
        
        self.isGlobalReport = value and True
    
#####################################################################
## Class Initialisation
# (none)
